package com.ef.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.ReplicationMode;
import org.hibernate.Session;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.ef.dao.Dao;
import com.ef.util.CustomExample;
import com.ef.util.PaginationSupport;


@SuppressWarnings("unchecked")
public class DaoImpl<T> extends HibernateDaoSupport implements Dao<T> {
	protected final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
			.getLog(DaoImpl.class);

	private Class<T> entityClass;

	public DaoImpl() {
		try {
			entityClass = (Class<T>) ((ParameterizedType) getClass()
					.getGenericSuperclass()).getActualTypeArguments()[0];
		} catch (Exception ex) {
			// 忽略
		}
	}
	
	@SuppressWarnings("unchecked")
	public List<T> findMultiAll(final String hql, final int startIndex, final int numPerPage) {
		List<T> list = this.getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query query = session.createQuery(hql);
				query.setFirstResult(startIndex);
				query.setMaxResults(numPerPage);
				List list = query.list();
				return list;
			}
		});
		return list;
	}

	public T uniqueResult(List<T> items) {
		if (items == null)
			return null;
		int size = items.size();

		switch (size) {
		case 1:
			return items.get(0);
		case 0:
			return null;
		default:
			log.warn("预计获取一条数据，实际获取了多条");
			return null;
		}
	}

	public int countByCriteria(final DetachedCriteria detachedCriteria) {
		return (Integer) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Criteria criteria = detachedCriteria
								.getExecutableCriteria(session);

						int totalCount = ((Integer) criteria.setProjection(
								Projections.rowCount()).uniqueResult())
								.intValue();
						return totalCount;
					}
				});
	}

	public int countByExample(final CustomExample<T> example) {
		return (Integer) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Criteria executableCriteria = session
								.createCriteria(example.getEntityClass());

						executableCriteria.add(example);

						example.appendCondition(executableCriteria);

						int totalCount = ((Integer) executableCriteria
								.setProjection(Projections.rowCount())
								.uniqueResult()).intValue();
						return totalCount;
					}
				});
	}

	public void delete(T entity) {
		this.getHibernateTemplate().delete(entity);
	}

	public void deleteAll(Collection<T> entities) {
		this.getHibernateTemplate().deleteAll(entities);
	}

	public void evict(T entity) {
		this.getHibernateTemplate().evict(entity);
	}

	public List<T> findAll() {
		return (List<T>) this.getSession().createCriteria(entityClass).list();
	}

	public PaginationSupport<T> findAll(int startIndex, int pageSize) {
		return findPageByCriteria(DetachedCriteria.forClass(entityClass),
				startIndex, pageSize);
	}

	public List<T> findAllByCriteria(final DetachedCriteria detachedCriteria) {
		return (List<T>) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Criteria criteria = detachedCriteria
								.getExecutableCriteria(session);
						return criteria.list();
					}
				});
	}

	public List<T> findByCriteria(Criterion... criterion) {
		DetachedCriteria detachedCrit = DetachedCriteria
				.forClass(getEntityClass());
		for (Criterion c : criterion) {
			detachedCrit.add(c);
		}
		return (List<T>) getHibernateTemplate().findByCriteria(detachedCrit);
	}

	public List<T> findAllByCriteria(final DetachedCriteria detachedCriteria,final int max){
		return (List<T>) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Criteria criteria = detachedCriteria
								.getExecutableCriteria(session).setMaxResults(max);
						return criteria.list();
					}
				});
	}
	
	public T findById(Serializable id) {
		return (T) this.getHibernateTemplate().get(entityClass, id);
	}

	public PaginationSupport<T> findPageByCriteria(
			final DetachedCriteria detachedCriteria, final int startIndex,
			final int pageSize) {
		return findPageByCriteria(detachedCriteria, null, startIndex, pageSize);
	}

	public PaginationSupport<T> findPageByCriteria(
			final DetachedCriteria detachedCriteria, final Order[] orders,
			final int startIndex, final int pageSize) {
		return (PaginationSupport<T>) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Criteria criteria = detachedCriteria
								.getExecutableCriteria(session);

						Integer integer = ((Integer) criteria.setProjection(
								Projections.rowCount()).uniqueResult());

						int totalCount = 10;
						if (integer != null)
							totalCount = integer.intValue();
						criteria.setProjection(null);
						criteria
								.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);

						for (int i = 0; orders != null && i < orders.length; i++) {
							criteria.addOrder(orders[i]);
						}

						List items = criteria.setFirstResult(startIndex)
								.setMaxResults(pageSize).list();

						PaginationSupport ps = new PaginationSupport(items,
								totalCount, startIndex, pageSize);
						return ps;
					}
				});
	}

	public PaginationSupport<T> findPageByExample(
			final CustomExample<T> example, final int startIndex,
			final int pageSize) {
		return findPageByExample(example, null, startIndex, pageSize);
	}

	public PaginationSupport<T> findPageByExample(
			final CustomExample<T> example, final Order[] orders,
			final int startIndex, final int pageSize) {
		return (PaginationSupport) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Criteria executableCriteria = session
								.createCriteria(example.getEntityClass());

						executableCriteria.add(example);

						example.appendCondition(executableCriteria);

						int totalCount = ((Integer) executableCriteria
								.setProjection(Projections.rowCount())
								.uniqueResult()).intValue();

						executableCriteria.setProjection(null);
						executableCriteria
								.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);

						for (int i = 0; orders != null && i < orders.length; i++) {
							executableCriteria.addOrder(orders[i]);
						}

						List items = executableCriteria.setFirstResult(
								startIndex).setMaxResults(pageSize).list();

						PaginationSupport ps = new PaginationSupport(items,
								totalCount, startIndex, pageSize);
						return ps;
					}
				});
	}

	public void flush() {
		this.getHibernateTemplate().flush();
	}

	public int getCountByCriteria(final DetachedCriteria detachedCriteria) {
		Integer count = (Integer) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Criteria criteria = detachedCriteria
								.getExecutableCriteria(session);
						return criteria.setProjection(Projections.rowCount())
								.uniqueResult();
					}
				});
		return count.intValue();
	}

	public Class<T> getEntityClass() {
		return entityClass;
	}

	public void merge(T entity) {
		this.getHibernateTemplate().merge(entity);
	}

	public void persist(T entity) {
		this.getHibernateTemplate().persist(entity);
	}

	public void refresh(T entity) {
		this.getHibernateTemplate().refresh(entity);
	}

	public void replicate(T entity, ReplicationMode replicationMode) {
		this.getHibernateTemplate().replicate(entity, replicationMode);
	}

	public void save(T entity) {
		this.getHibernateTemplate().persist(entity);
	}

	public void saveOrUpdate(T entity) {
		this.getHibernateTemplate().saveOrUpdate(entity);
	}

	public void saveOrUpdateAll(Collection<T> entities) {
		this.getHibernateTemplate().saveOrUpdateAll(entities);
	}

	public void update(T entity) {
		this.getHibernateTemplate().update(entity);
	}
	
	public void clear(){
		this.getHibernateTemplate().clear();
	}
}
